//
//  FormLoader.m
//  iBasicRuntime
//
//  Created by Andrew Barry on 21/02/09.
//  Copyright 2009 Barry Computer Services. All rights reserved.
//

#import "FormLoader.h"
#import "control.h"
#import "ActionBridge.h"
#ifdef MACOSX
#import "FlippedView.h"
#endif
#import "runtime.h"
#import "form.h"
#import "CanvasView.h"
#import "TableView.h"
#import "timer.h"
#import "data.h"
#import "NSRadioButtonICM.h"
#import "RadioActionBridge.h"
#import "WrappedJsval.h"
#ifdef MACOSX
#import "WebKit/WebView.h"
#endif
#ifdef PHONEOSX
#import "touchControl.h"
#endif

static void copyStringProperty(JSObject *obj, NSDictionary *defn, NSString *fieldName) {
	NSString *val = [defn objectForKey:fieldName];
	if (!val)
		return;
	jsval prop = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, [val UTF8String]));
	JS_SetProperty(cx, obj, [fieldName UTF8String], &prop);
}


@implementation FormLoader

-(void)compileForm:(NSDictionary *)formContent withFile: (NSString *) filename {
	compileEventHandler([NSString stringWithFormat: @"%@/_form",filename], @"Load", formContent);
	compileEventHandler([NSString stringWithFormat: @"%@/_form",filename], @"Unload", formContent);
	compileEventHandler([NSString stringWithFormat: @"%@/_form",filename], @"EnableControls", formContent);

	NSArray *saveControls = [formContent objectForKey:@"controls"];
	for (int idx = 0; idx < [saveControls count]; idx++) {
		NSDictionary *ctlDefn = [saveControls objectAtIndex: idx];
		[self compileControl:ctlDefn withFile: filename];
	}
}

-(void) compileControl: (NSDictionary *)defn  withFile: (NSString *) filename {
	NSString *controlName = [defn objectForKey:@"name"];
	NSString *typeName = [defn objectForKey:@"type"];
	NSString *objectName = [NSString stringWithFormat: @"%@/%@",filename,controlName];
	
	if ([typeName isEqualToString:@"button"]) {
		compileEventHandler(objectName, @"Click", defn);
	} else if ([typeName isEqualToString:@"checkbox"]) {
			compileEventHandler(objectName, @"Click", defn);
	} else if ([typeName isEqualToString:@"radio"]) {
		compileEventHandler(objectName, @"Click", defn);
	} else if ([typeName isEqualToString:@"box"]) {
		compileEventHandler(objectName, @"Click", defn);
	} else if ([typeName isEqualToString:@"data"]) {
		compileEventHandler(objectName, @"Changed", defn);
	} else if ([typeName isEqualToString:@"popup"]) {
		compileEventHandler(objectName, @"Click", defn);
	} else if ([typeName isEqualToString:@"progress"]) {
		compileEventHandler(objectName, @"Click", defn);
	} else if ([typeName isEqualToString:@"canvas"]) {
		compileEventHandler(objectName, @"Paint", defn);
	} else if ([typeName isEqualToString:@"timer"]) {
		compileEventHandler(objectName, @"Action", defn);
	} else if ([typeName isEqualToString:@"tableview"]) {
		compileEventHandler(objectName, @"RowCount", defn);
		compileEventHandler(objectName, @"Cell", defn);
		compileEventHandler(objectName, @"SelectionChanged", defn);
		compileEventHandler(objectName, @"DoubleClick", defn);
	}
}

#ifdef MACOSX
-(NSWindow *) newPanel:(NSDictionary *)form {
	int width = [[form objectForKey:@"width"] intValue];
	int height = [[form objectForKey:@"height"] intValue];
	NSRect r;
	r = NSMakeRect(100, 100, width, height);
	int styles = NSTitledWindowMask | NSResizableWindowMask | NSUtilityWindowMask;
	
	NSWindow *wnd = [[NSPanel alloc] initWithContentRect:r styleMask:styles backing:NSBackingStoreBuffered defer:NO];
	
	[wnd setTitle:[form objectForKey:@"title"]];
	
	NSView *content = [[FlippedView alloc] init];
	
	//	[content setWantsLayer:YES];
	[wnd setContentView:content];
	return wnd;
}


-(NSWindow *) newWindow:(NSDictionary *)form {
	int width = [[form objectForKey:@"width"] intValue];
	int height = [[form objectForKey:@"height"] intValue];
	NSRect r;
	r = NSMakeRect(100, 100, width, height);
	int styles = NSTitledWindowMask;
	
	if ([[form objectForKey:@"canResize"] boolValue])
		styles |= NSResizableWindowMask;
	
	if ([[form objectForKey:@"canClose"] boolValue])
		styles |= NSClosableWindowMask;
	
	NSWindow *wnd = [[NSWindow alloc] initWithContentRect:r styleMask:styles backing:NSBackingStoreBuffered defer:NO];
	
	[wnd setTitle:[form objectForKey:@"title"]];
	
	FlippedView *content = [[FlippedView alloc] init];
	
	if ([[form objectForKey:@"hasBackgroundColor"] boolValue]) {
		NSString *strBackgroundColor = [form objectForKey:@"backgroundColor"];
		if (strBackgroundColor) {
			NSArray *bits = [strBackgroundColor componentsSeparatedByString: @"/"];
			float r = [[bits objectAtIndex:0] floatValue];
			float g = [[bits objectAtIndex:1] floatValue];
			float b = [[bits objectAtIndex:2] floatValue];
			NSColor *col = [NSColor colorWithCalibratedRed:r green:g blue: b alpha: 1.0f];
			[content setBackgroundColor:col];
		}
	}
	
//	[content setWantsLayer:YES];
	[wnd setContentView:content];
	return wnd;
}
#endif

#ifdef MACOSX
-(JSObject *)loadForm:(NSDictionary *)formContent into: (NSWindow *) wnd {
#endif
#ifdef PHONEOSX
	-(JSObject *)loadForm:(NSDictionary *)formContent into: (UIView *) wnd {
#endif

#ifdef MACOSX
		NSView *parent = [wnd contentView];
#endif
#ifdef PHONEOSX
		UIView *parent = wnd;
#endif
		
		JSObject *obj = bindForm(cx, wnd);
	JS_AddRoot(cx, &obj);
	
	NSArray *saveControls = [formContent objectForKey:@"controls"];
	int idx;

	dataSourceLoadMap = [NSMutableDictionary new];
	
	NSMutableDictionary *nameToView = [NSMutableDictionary new];
	for (idx = 0; idx < [saveControls count]; idx++) {
		NSDictionary *ctlDefn = [saveControls objectAtIndex: idx];
		NSString *name = [ctlDefn objectForKey:@"name"];
#ifdef MACOSX
		NSView *view = [self loadControl:ctlDefn withObject: obj];
#endif
#ifdef PHONEOSX
		UIView *view = [self loadControl:ctlDefn withObject: obj];
#endif
		if (view != nil) {
			if (name)
				[nameToView setObject:view forKey:name];
			if ([ctlDefn objectForKey:@"visible"])
				[view setHidden:![[ctlDefn objectForKey:@"visible"] boolValue]];
#ifdef MACOSX
			[self loadSizing: view withDefn: ctlDefn];
#endif
			
			NSString *parentName = [ctlDefn objectForKey: @"parent"];
			if (parentName) {
#ifdef MACOSX
				NSView *parentView = [nameToView objectForKey:parentName];
#else
				UIView *parentView = [nameToView objectForKey:parentName];
#endif
				if (parentView)
					[parentView addSubview: view];
				else
					[parent addSubview: view];
			} else {
				[parent addSubview:view];
			}
			
		}
	}
	
//	[formContent setValue:@"bob" forKey: @"testy"];
//	NSString *t = [formContent valueForKey:@"testy"];
	
	applyEventHandler(obj, @"Load", formContent);
	applyEventHandler(obj, @"EnableControls", formContent);
	applyEventHandler(obj, @"Unload", formContent);
	
	jsval ctlProp = JSVAL_VOID;
	jsval dataSourceProp = JSVAL_VOID;
	
	JS_AddRoot(cx, &ctlProp);
	JS_AddRoot(cx, &dataSourceProp);
	
	NSArray *loadMapControls = [dataSourceLoadMap allKeys];
	for (int idx = 0; idx < [loadMapControls count]; idx++) {
		NSString *controlName = [loadMapControls objectAtIndex:idx];
		ctlProp = JSVAL_VOID;
		JS_GetProperty(cx, obj, [controlName UTF8String], &ctlProp);
		if (JSVAL_IS_OBJECT(ctlProp)) {
			NSString *dataSourceName = [dataSourceLoadMap objectForKey:controlName];
			dataSourceProp = JSVAL_VOID;
			JS_GetProperty(cx, obj, [dataSourceName UTF8String], &dataSourceProp);
			if (JSVAL_IS_OBJECT(dataSourceProp)) {
				JS_SetProperty(cx, JSVAL_TO_OBJECT(ctlProp), "dataSource", &dataSourceProp);
			}
		}
	}
	
	JS_RemoveRoot(cx, &ctlProp);
	JS_RemoveRoot(cx, &dataSourceProp);

	[dataSourceLoadMap release];
	dataSourceLoadMap = nil;
	
	JS_RemoveRoot(cx, &obj);
	
	return obj;
}

-(void)startForm:(JSObject *) obj {
	JS_AddRoot(cx, &obj);
	jsval prop, rv;
	if (JS_GetProperty(cx, obj, "Load",	&prop)) {
		if (JSVAL_IS_OBJECT(prop)) {
			JS_CallFunctionValue(cx, obj, prop, 0, nil, &rv);
		}
	}
	
	if (JS_GetProperty(cx, obj, "EnableControls",	&prop)) {
		if (JSVAL_IS_OBJECT(prop))
			JS_CallFunctionValue(cx, obj, prop, 0, nil, &rv);
	}
	
	JS_RemoveRoot(cx, &obj);
}
	
#ifdef MACOSX
-(void) loadSizing: (NSView *) ctl withDefn: (NSDictionary *) defn {
	bool lockLeft = [[defn objectForKey:@"lockLeft"] boolValue];
	bool lockTop = [[defn objectForKey:@"lockTop"] boolValue];
	bool lockRight = [[defn objectForKey:@"lockRight"] boolValue];
	bool lockBottom = [[defn objectForKey:@"lockBottom"] boolValue];
	
	int mask = 0;
	if (lockRight && lockLeft)
		mask |= NSViewWidthSizable;
	else if (lockRight)
		mask |= NSViewMinXMargin;
	if (lockTop && lockBottom)
		mask |= NSViewHeightSizable;
	else if (lockBottom)
		mask |= NSViewMinYMargin;
	[ctl setAutoresizingMask: mask];
}
	
-(NSView *) loadControl: (NSDictionary *)defn withObject: (JSObject *) root {
	NSString *typeName = [defn objectForKey:@"type"];
	NSString *dataSourceName = nil;
	NSRect frame;
	frame.origin.x = [(NSNumber *) [defn objectForKey:@"left"] intValue];
	frame.origin.y = [(NSNumber *) [defn objectForKey:@"top"] intValue];
	frame.size.width = [(NSNumber *) [defn objectForKey:@"width"] intValue];
	frame.size.height = [(NSNumber *) [defn objectForKey:@"height"] intValue];
	
	NSString *name = [defn objectForKey: @"name"];
	jsval rootval = OBJECT_TO_JSVAL(root);
	
	if ([typeName isEqualToString:@"label"]) {
		NSTextField *lbl = [[NSTextField alloc] initWithFrame:frame];
		[lbl setStringValue:[defn objectForKey:@"text"]];
		[lbl setEditable:[[defn objectForKey: @"editable"] boolValue]];
		[lbl setSelectable:[[defn objectForKey: @"selectable"] boolValue]];
		[lbl setBezeled:NO];
		[lbl setDrawsBackground:NO];
		[lbl setFont:[NSFont controlContentFontOfSize: 13]];

		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindTextField(cx, lbl);
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_RemoveRoot(cx, &obj);		
		
		return lbl;
	} else if ([typeName isEqualToString:@"textfield"]) {
		NSTextField *lbl;
		if ([[defn objectForKey: @"password"] boolValue])
			lbl = [[NSSecureTextField alloc] initWithFrame:frame];
		else
			lbl = [[NSTextField alloc] initWithFrame:frame];
		[lbl setStringValue:[defn objectForKey:@"text"]];
		[lbl setEditable:[[defn objectForKey: @"editable"] boolValue]];
		[lbl setSelectable:[[defn objectForKey: @"selectable"] boolValue]];
		[lbl setFont:[NSFont controlContentFontOfSize: 13]];
		[[lbl cell] setWraps: NO];
		[[lbl cell] setScrollable: YES];
		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindTextField(cx, lbl);
		
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);

		dataSourceName = [defn objectForKey:@"dataSource"];
		if (dataSourceName) {
			[dataSourceLoadMap setObject:dataSourceName forKey:name];
		}

		copyStringProperty(obj, defn, @"dataField");
		JS_RemoveRoot(cx, &obj);		
		
		return lbl;
	} else if ([typeName isEqualToString:@"checkbox"]) {
		NSButton *btn = [[NSButton alloc] initWithFrame:frame];
		[btn setButtonType:NSSwitchButton];
		[btn setTitle:[defn objectForKey:@"caption"]];
		[btn setEnabled: [[defn objectForKey:@"enabled"] boolValue]];
		[btn setIntValue: [[defn objectForKey:@"checked"] intValue]];

		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindButton(cx, btn);
		
		applyEventHandler(obj, @"Click", defn);
		
		copyStringProperty(obj, defn, @"type");

		ActionBridge *bridge = [[ActionBridge alloc] initWithObject:obj withEvent:@"Click" withContext:cx withTarget:root];
		[btn setAction:@selector(doIt:)];
		[btn setTarget:bridge];
		
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);

		dataSourceName = [defn objectForKey:@"dataSource"];
		if (dataSourceName) {
			[dataSourceLoadMap setObject:dataSourceName forKey:name];
		}
		
		copyStringProperty(obj, defn, @"dataField");
		
		JS_RemoveRoot(cx, &obj);		
		
		return btn;
	} else if ([typeName isEqualToString:@"radio"]) {
		NSRadioButtonICM *btn = [[NSRadioButtonICM alloc] initWithFrame:frame];
		[btn setButtonType:NSRadioButton];
		[btn setTitle:[defn objectForKey:@"caption"]];
		[btn setEnabled: [[defn objectForKey:@"enabled"] boolValue]];

		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindButton(cx, btn);
		btn->radio = [[WrappedJsval alloc] initWithVal:OBJECT_TO_JSVAL(obj)];
		
		applyEventHandler(obj, @"Click", defn);
		
		RadioActionBridge *bridge = [[RadioActionBridge alloc] initWithObject:obj withEvent:@"Click" withContext:cx withTarget:root];
		[btn setAction:@selector(doIt:)];
		[btn setTarget:bridge];
		copyStringProperty(obj, defn, @"value");

		if (name) {
			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
		}
		JS_RemoveRoot(cx, &obj);		
		
		return btn;
	} else if ([typeName isEqualToString:@"canvas"]) {
		CanvasView *btn = [[CanvasView alloc] initWithFrame:frame];

		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindView(cx, btn);
		
		[btn setObject:obj withTarget: root];
		
		applyEventHandler(obj, @"Paint", defn);
		
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_SetProperty(cx, obj, "form", &rootval);

		JS_RemoveRoot(cx, &obj);		
		
		return btn;
	} else if ([typeName isEqualToString:@"box"]) {
		NSBox *btn = [[NSBox alloc] initWithFrame:frame];
		[btn setContentView:[FlippedView new]];
		[btn setTitle:[defn objectForKey:@"caption"]];
		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindBox(cx, btn);
				
		applyEventHandler(obj, @"Click", defn);
		
		dataSourceName = [defn objectForKey:@"dataSource"];
		if (dataSourceName) {
			[dataSourceLoadMap setObject:dataSourceName forKey:name];
		}
		copyStringProperty(obj, defn, @"dataField");
				
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_SetProperty(cx, obj, "form", &rootval);
		JS_RemoveRoot(cx, &obj);		
		
		return btn;
	} else if ([typeName isEqualToString:@"popup"]) {
		NSPopUpButton *btn = [[NSPopUpButton alloc] initWithFrame:frame];
		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindPopup(cx, btn);
		
		applyEventHandler(obj, @"Click", defn);
		
		ActionBridge *bridge = [[ActionBridge alloc] initWithObject:obj withEvent:@"Click" withContext:cx withTarget:root];
		[btn setAction:@selector(doIt:)];
		[btn setTarget:bridge];
		
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_SetProperty(cx, obj, "form", &rootval);
		JS_RemoveRoot(cx, &obj);		
		
		return btn;
	} else if ([typeName isEqualToString:@"web"]) {
		WebView *btn = [[WebView alloc] initWithFrame:frame frameName:nil groupName:nil];

		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindWeb(cx, btn);
			
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_SetProperty(cx, obj, "form", &rootval);

		JS_RemoveRoot(cx, &obj);		
		
		return btn;
	} else if ([typeName isEqualToString:@"spinner"]) {
		NSProgressIndicator *btn = [[NSProgressIndicator alloc] initWithFrame:frame];
		[btn setStyle:NSProgressIndicatorSpinningStyle];
		[btn startAnimation:self];
		
		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindView(cx, btn);
		
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_SetProperty(cx, obj, "form", &rootval);

		JS_RemoveRoot(cx, &obj);
		return btn;
	} else if ([typeName isEqualToString:@"progress"]) {
		NSProgressIndicator *btn = [[NSProgressIndicator alloc] initWithFrame:frame];
		
		[btn setMinValue:[[defn objectForKey:@"minimum"] doubleValue]];
		[btn setMaxValue:[[defn objectForKey:@"maximum"] doubleValue]];
		[btn setDoubleValue:[[defn objectForKey:@"value"] doubleValue]];
		[btn setIndeterminate:NO];
		
		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindProgress(cx, btn);
		
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_SetProperty(cx, obj, "form", &rootval);

		JS_RemoveRoot(cx, &obj);
		return btn;
	} else if ([typeName isEqualToString:@"timer"]) {
		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = createTimerForWindow(root);
		applyEventHandler(obj, @"Action", defn);
		int period = [[defn objectForKey:@"period"] intValue];
		bool repeating = [[defn objectForKey:@"repeating"] boolValue];
		jsval v = INT_TO_JSVAL(period);
		JS_SetProperty(cx, obj, "period", &v);
		v = BOOLEAN_TO_JSVAL(repeating);
		JS_SetProperty(cx, obj, "repeating", &v);

		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_SetProperty(cx, obj, "form", &rootval);
		JS_RemoveRoot(cx, &obj);

		return nil;
	} else if ([typeName isEqualToString:@"data"]) {
		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = createDataObjectForWindow(root);
		applyEventHandler(obj, @"Changed", defn);
		
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_SetProperty(cx, obj, "form", &rootval);
		JS_RemoveRoot(cx, &obj);
		
		return nil;
	} else if ([typeName isEqualToString:@"tableview"]) {
		TableView *btn = [[TableView alloc] initWithFrame:frame];
		
		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindTable(cx, btn);
		
		[btn setObject:obj withTarget: root];
		
		applyEventHandler(obj, @"RowCount", defn);
		applyEventHandler(obj, @"Cell", defn);
		applyEventHandler(obj, @"SelectionChanged", defn);
		applyEventHandler(obj, @"DoubleClick", defn);
		
		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);		
		JS_SetProperty(cx, obj, "form", &rootval);

		dataSourceName = [defn objectForKey:@"dataSource"];
		if (dataSourceName) {
			[dataSourceLoadMap setObject:dataSourceName forKey:name];
		}
		copyStringProperty(obj, defn, @"dataField");

		JS_RemoveRoot(cx, &obj);		
		
		[btn awakeFromDefn: defn];
		return btn;
	} else {
		NSButton *btn = [[NSButton alloc] initWithFrame:frame];
		[btn setButtonType:NSMomentaryPushInButton];
		[btn setBezelStyle:NSRoundedBezelStyle];
		[btn setFont:[NSFont controlContentFontOfSize: 13]];
		[btn setTitle:[defn objectForKey:@"caption"]];
		[btn setEnabled: [[defn objectForKey:@"enabled"] boolValue]];

		int shortcut = [[defn objectForKey:@"shortcut"] intValue];
		if (shortcut == 1)
			[btn setKeyEquivalent:@"\r"];
		else if (shortcut == 2)
			[btn setKeyEquivalent:@"\e"];
		
		JSObject *obj = nil;
		JS_AddRoot(cx, &obj);
		obj = bindButton(cx, btn);

		applyEventHandler(obj, @"Click", defn);
		
		ActionBridge *bridge = [[ActionBridge alloc] initWithObject:obj withEvent:@"Click" withContext:cx withTarget:root];
		[btn setAction:@selector(doIt:)];
		[btn setTarget:bridge];

		jsval jv = OBJECT_TO_JSVAL(obj);
		JS_SetProperty(cx, root, [name UTF8String], &jv);
		JS_SetProperty(cx, obj, "form", &rootval);
		JS_RemoveRoot(cx, &obj);		
		
		return btn;
	}
	
	return nil;
}
#endif

#ifdef PHONEOSX
	-(UIView *) loadControl: (NSDictionary *)defn withObject: (JSObject *) root {
		NSString *typeName = [defn objectForKey:@"type"];
		NSString *dataSourceName = nil;
		CGRect frame;
		
		frame.origin.x = [(NSNumber *) [defn objectForKey:@"left"] intValue];
		frame.origin.y = [(NSNumber *) [defn objectForKey:@"top"] intValue];
		frame.size.width = [(NSNumber *) [defn objectForKey:@"width"] intValue];
		frame.size.height = [(NSNumber *) [defn objectForKey:@"height"] intValue];
		
		NSString *name = [defn objectForKey: @"name"];
		jsval rootval = OBJECT_TO_JSVAL(root);
		
		if ([typeName isEqualToString:@"label"]) {
			UILabel *lbl = [[UILabel alloc] initWithFrame:frame];
			lbl.text = [defn objectForKey:@"text"];
			
			JSObject *obj = nil;
			JS_AddRoot(cx, &obj);
			obj = bindTextField(cx, lbl);
			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
			JS_RemoveRoot(cx, &obj);		
			
			return lbl;
		} else if ([typeName isEqualToString:@"textfield"]) {
			UITextField *lbl;
			lbl = [[UITextField alloc] initWithFrame:frame];
			lbl.text = [defn objectForKey:@"text"];
			lbl.enabled = [[defn objectForKey: @"editable"] boolValue];
			JSObject *obj = nil;
			JS_AddRoot(cx, &obj);
			obj = bindTextField(cx, lbl);
			
			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
			
			dataSourceName = [defn objectForKey:@"dataSource"];
			if (dataSourceName) {
				[dataSourceLoadMap setObject:dataSourceName forKey:name];
			}
			
			copyStringProperty(obj, defn, @"dataField");
			JS_RemoveRoot(cx, &obj);		
			
			return lbl;
		} else if ([typeName isEqualToString:@"checkbox"]) {
			UISwitch *btn = [[UISwitch alloc] initWithFrame: frame];
			btn.enabled = [[defn objectForKey:@"enabled"] boolValue];
			btn.on = [[defn objectForKey:@"checked"] intValue] != 0;
			
			JSObject *obj = nil;
			JS_AddRoot(cx, &obj);
			obj = bindButton(cx, btn);
			
			applyEventHandler(obj, @"Click", defn);
			
			copyStringProperty(obj, defn, @"type");
			/*
			ActionBridge *bridge = [[ActionBridge alloc] initWithObject:obj withEvent:@"Click" withContext:cx withTarget:root];
			[btn setAction:@selector(doIt:)];
			[btn setTarget:bridge];
			*/
			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
			
			dataSourceName = [defn objectForKey:@"dataSource"];
			if (dataSourceName) {
				[dataSourceLoadMap setObject:dataSourceName forKey:name];
			}
			
			copyStringProperty(obj, defn, @"dataField");
			
			JS_RemoveRoot(cx, &obj);		
			
			return btn;
		} else if ([typeName isEqualToString:@"canvas"]) {
			CanvasView *btn = [[CanvasView alloc] initWithFrame:frame];
			
			JSObject *obj = nil;
			JS_AddRoot(cx, &obj);
			obj = bindView(cx, btn);
			
			[btn setObject:obj withTarget: root];
			
			applyEventHandler(obj, @"Paint", defn);
			
			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
			JS_SetProperty(cx, obj, "form", &rootval);
			
			JS_RemoveRoot(cx, &obj);		
			
			return btn;
		} else if ([typeName isEqualToString:@"spinner"]) {
			UIActivityIndicatorView *btn = [[UIActivityIndicatorView alloc] initWithFrame:frame];
			[btn startAnimating];
			
			JSObject *obj = nil;
			JS_AddRoot(cx, &obj);
			obj = bindView(cx, btn);
			
			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
			JS_SetProperty(cx, obj, "form", &rootval);
			
			JS_RemoveRoot(cx, &obj);
			return btn;
		} else if ([typeName isEqualToString:@"progress"]) {
			UIProgressView *btn = [[UIProgressView alloc] initWithFrame:frame];
			/*
			[btn setMinValue:[[defn objectForKey:@"minimum"] doubleValue]];
			[btn setMaxValue:[[defn objectForKey:@"maximum"] doubleValue]];
			[btn setDoubleValue:[[defn objectForKey:@"value"] doubleValue]];
			[btn setIndeterminate:NO];
	*/		
			JSObject *obj = nil;
			JS_AddRoot(cx, &obj);
			obj = bindProgress(cx, btn);
			
			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
			JS_SetProperty(cx, obj, "form", &rootval);
			
			JS_RemoveRoot(cx, &obj);
			return btn;
		} else if ([typeName isEqualToString:@"timer"]) {
			JSObject *obj = nil;
			JS_AddRoot(cx, &obj);
			obj = createTimerForWindow(root);
			applyEventHandler(obj, @"Action", defn);
			int period = [[defn objectForKey:@"period"] intValue];
			bool repeating = [[defn objectForKey:@"repeating"] boolValue];
			jsval v = INT_TO_JSVAL(period);
			JS_SetProperty(cx, obj, "period", &v);
			v = BOOLEAN_TO_JSVAL(repeating);
			JS_SetProperty(cx, obj, "repeating", &v);
			
			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
			JS_SetProperty(cx, obj, "form", &rootval);
			JS_RemoveRoot(cx, &obj);
			
			return nil;
		} else if ([typeName isEqualToString:@"data"]) {
			JSObject *obj = nil;
			JS_AddRoot(cx, &obj);
			obj = createDataObjectForWindow(root);
			applyEventHandler(obj, @"Changed", defn);
			
			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
			JS_SetProperty(cx, obj, "form", &rootval);
			JS_RemoveRoot(cx, &obj);
			
			return nil;
		} else {
			UIButton *btn = [UIButton buttonWithType: UIButtonTypeRoundedRect];
			btn.frame = frame;
			[btn setTitle: [defn objectForKey:@"caption"] forState: UIControlStateNormal];
			btn.enabled = [[defn objectForKey:@"enabled"] boolValue];
			JSObject *obj = nil;
			JS_AddRoot(cx, &obj);
			obj = bindButton(cx, btn);
			
			applyEventHandler(obj, @"Click", defn);
			
			ActionBridge *bridge = [[ActionBridge alloc] initWithObject:obj withEvent:@"Click" withContext:cx withTarget:root];
			
			[btn addTarget:bridge action:@selector(doIt:) forControlEvents:UIControlEventTouchUpInside];
			
//			[btn setAction:@selector(doIt:)];
//			[btn setTarget:bridge];

			jsval jv = OBJECT_TO_JSVAL(obj);
			JS_SetProperty(cx, root, [name UTF8String], &jv);
			JS_SetProperty(cx, obj, "form", &rootval);
			JS_RemoveRoot(cx, &obj);		
			
			return btn;
		}
		
		return nil;
	}
#endif
	
@end
